#pragma once
#include "requestor.hpp"
#include <unordered_set>

namespace bitrpc {
    namespace client {

        //服务提供者：负责向注册中心注册服务，使得其他客户端能够发现并使用该服务
        class Provider {
            public:
                using ptr = std::shared_ptr<Provider>;

                //请求器对象，用于发送请求
                Provider(const Requestor::ptr &requestor) : _requestor(requestor){}
                bool registryMethod(const BaseConnection::ptr &conn, const std::string &method, const Address &host) {
                    auto msg_req = MessageFactory::create<ServiceRequest>();
                    msg_req->setId(UUID::uuid());
                    msg_req->setMType(MType::REQ_SERVICE);
                    msg_req->setMethod(method);
                    msg_req->setHost(host);
                    msg_req->setOptype(ServiceOptype::SERVICE_REGISTRY);

                    //发送请求并等待响应(onreponse)
                    BaseMessage::ptr msg_rsp;
                    bool ret = _requestor->send(conn, msg_req, msg_rsp);
                    if (ret == false) {
                        ELOG("%s 服务注册失败！", method.c_str());
                        return false;
                    }

                    //处理响应
                    auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                    if (service_rsp.get() == nullptr) {
                        ELOG("响应类型向下转换失败！");
                        return false;
                    }
                    if (service_rsp->rcode() != RCode::RCODE_OK) {
                        ELOG("服务注册失败，原因：%s", errReason(service_rsp->rcode()).c_str());
                        return false;
                    }
                    return true;
                }
            private:
                Requestor::ptr _requestor;  //请求器，用于发送请求
        };

        //方法主机管理：管理提供特定给服务方法的所有主机地址，支持负载均衡选择
        class MethodHost {
            public:
                using ptr = std::shared_ptr<MethodHost>;

                MethodHost(): _idx(0){} //创建一个空的主机列表

                MethodHost(const std::vector<Address> &hosts):
                    _hosts(hosts.begin(), hosts.end()), _idx(0){}

                //添加主机地址：当收到服务上限通知时调用，即将新上线的服务提供者添加到列表中
                void appendHost(const Address &host) {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _hosts.push_back(host);
                }

                //移除主机地址：当收到服务下线通知时调用，将下线的服务提供者冲列表中移除
                void removeHost(const Address &host) {
                    std::unique_lock<std::mutex> lock(_mutex);
                    for (auto it = _hosts.begin(); it != _hosts.end(); ++it) {
                        if (*it == host) {
                            _hosts.erase(it);
                            break;
                        }
                    }
                }
                //选择一个主机地址：使用轮询算法选择一个服务提供者地址，实现简单的负载均衡
                Address chooseHost() {
                    std::unique_lock<std::mutex> lock(_mutex);
                    size_t pos = _idx++ % _hosts.size();
                    return _hosts[pos];
                }
                //检查主机列表是否为空
                bool empty() {
                    std::unique_lock<std::mutex> lock(_mutex);
                    return _hosts.empty();
                }
            private:
                std::mutex _mutex;
                size_t _idx;                        //当前选择索引，用于轮询算法
                std::vector<Address> _hosts;        //提供服务的主机地址列表
        };

        //服务发现类：负责发现服务并管理服务提供者信息，处理服务上线/下线通知
        class Discoverer {
            public:
                using OfflineCallback = std::function<void(const Address&)>;    //服务下线回调函数
                using ptr = std::shared_ptr<Discoverer>;

                Discoverer(const Requestor::ptr &requestor, const OfflineCallback &cb) : 
                    _requestor(requestor), _offline_callback(cb){}

                //服务发现：尝试发现提供特定服务主机，优先使用缓存，缓存不存在则向注册中心查询
                bool serviceDiscovery(const BaseConnection::ptr &conn, const std::string &method, Address &host) {
                    {
                        //检查本地缓存，如果已有该服务的提供者信息，直接使用
                        std::unique_lock<std::mutex> lock(_mutex);
                        auto it = _method_hosts.find(method);
                        if (it != _method_hosts.end()) {
                            if (it->second->empty() == false) {
                                host = it->second->chooseHost();
                                return true;
                            }
                        }
                    }
                    //本地没有缓存或缓存为空，向注册中心发送服务发现请求
                    auto msg_req = MessageFactory::create<ServiceRequest>();
                    msg_req->setId(UUID::uuid());
                    msg_req->setMType(MType::REQ_SERVICE);
                    msg_req->setMethod(method);
                    msg_req->setOptype(ServiceOptype::SERVICE_DISCOVERY);

                    //发送请求并等待响应
                    BaseMessage::ptr msg_rsp;
                    bool ret = _requestor->send(conn, msg_req, msg_rsp);
                    if (ret == false) {
                        ELOG("服务发现失败！");
                        return false;
                    }

                    //处理响应
                    auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                    if (!service_rsp) {
                        ELOG("服务发现失败！响应类型转换失败！");
                        return false;
                    }
                    if (service_rsp->rcode() != RCode::RCODE_OK) {
                        ELOG("服务发现失败！%s", errReason(service_rsp->rcode()).c_str());
                        return false;
                    }
                    //能走到这里，代表当前是没有对应的服务提供主机的：更新本地缓存并选择一个服务提供者
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto method_host = std::make_shared<MethodHost>(service_rsp->hosts());
                    if (method_host->empty()) {
                        ELOG("%s 服务发现失败！没有能够提供服务的主机！", method.c_str());
                        return false;
                    }
                    host = method_host->chooseHost();
                    _method_hosts[method] = method_host;
                    return true;
                }
                //处理服务状态变更请求：处理注册中心发来的服务上线和下线通知，更新本地缓存
                //这个接口时提供给Dispathcher模块进行服务上线下线请求处理的回调函数
                void onServiceRequest(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg) {
                    //1. 判断是上线还是下线请求，如果都不是那就不用处理了
                    auto optype = msg->optype();
                    std::string method = msg->method();
                    std::unique_lock<std::mutex> lock(_mutex);
                    if (optype == ServiceOptype::SERVICE_ONLINE){
                        //2. 上线请求：找到MethodHost，向其中新增一个主机地址
                        auto it = _method_hosts.find(method);
                        if (it == _method_hosts.end()) {
                            //如果本地没有该服务的存储，创建新的缓存
                            auto method_host = std::make_shared<MethodHost>();
                            method_host->appendHost(msg->host());
                            _method_hosts[method] = method_host;
                        }else {
                            //人如果已有缓存，添加新上线的服务提供者
                            it->second->appendHost(msg->host());
                        }
                    } else if (optype == ServiceOptype::SERVICE_OFFLINE){
                        //3. 下线请求：找到MethodHost，从其中删除一个主机地址
                        auto it = _method_hosts.find(method);
                        if (it == _method_hosts.end()) {
                            return;
                        }
                        //从缓存中移除下线的服务提供者，并触发下线回调
                        it->second->removeHost(msg->host());
                        _offline_callback(msg->host());
                    }
                }
            private:
                OfflineCallback _offline_callback;                                  //服务下线回调函数
                std::mutex _mutex;                                                  //互斥，保证线程安全
                std::unordered_map<std::string, MethodHost::ptr> _method_hosts;     //服务方法到主机管理器的映射
                Requestor::ptr _requestor;                                          //请求器，用于发送请求
        };  
    }
}